/*

停车场管理系统（扩展版）
    我们来扩展之前的停车场管理系统，添加一个停车场管理员的角色。
    管理员的职责是监控停车场的状态，只有当停车场有空位时才允许车辆进入。
    车辆进入和离开停车场都需要加锁，确保同时只有一个线程在操作停车位状态。
    同时使用信号量来控制停车场的容量，使用互斥锁来保护共享数据（停车位状态）。


    信号量 管理 停车场的个数

    互斥锁 用于同时只能一个停车

    条件变量 用于控制通知其他车辆可以停车
*/

#include <head.h>

// 停车场
int Parking_Lots = 5; // 停车场初始数量

int sem_num = 0;

// 初始化信号量
sem_t Sem_Car;             // 信号量
pthread_mutex_t Mutex_Car; // 互斥锁
pthread_cond_t Cond_Car;   // 条件变量

// 线程
#define Car_Num 50
pthread_t Admin_Tid;            // 管理员线程
pthread_t Car_Tid[Car_Num];     // 车辆线程
pthread_t Car_func_id[Car_Num]; // 车辆函数编号

// 线程函数声明
void *Admin_func(void *val); // 管理员函数
void *Car_func(void *val);   // 车辆函数

int main(int argc, char const *argv[])
{
    // 初始化 互斥锁
    pthread_mutex_init(&Mutex_Car, NULL);
    // 初始化 信号量
    sem_init(&Sem_Car, 0, 5);
    // 初始化 条件变量
    pthread_cond_init(&Cond_Car, NULL);

    // 创建 管理员 线程
    pthread_create(&Admin_Tid, NULL, Admin_func, NULL);
    // 创建 车辆 线程
    for (size_t i = 0; i < Car_Num; i++)
    {
        Car_func_id[i] = i + 1;
        pthread_create(&Car_Tid[i], NULL , Car_func , (void * )& Car_func_id[i]);
    }

    // 回收 管理员 线程
    pthread_join(Admin_Tid , NULL);
    // 回收 车辆 线程
    for (size_t i = 0; i < Car_Num; i++)
    {
        pthread_join(Car_Tid[i], NULL );
    }

    // 销毁 互斥锁
    pthread_mutex_destroy(&Mutex_Car);
    // 销毁 信号量
    sem_destroy(&Sem_Car);
    // 销毁 条件变量
    pthread_cond_destroy(&Cond_Car);
    return 0;
}

// 线程函数声明
void *Admin_func(void *val) // 管理员函数
{
    // 互斥锁 上锁
    pthread_mutex_lock(&Mutex_Car);

    // 条件变量   通知车辆可以进场
    printf("管理员: 有空车位 可以进场\n");

    sem_num = 1;
    pthread_cond_signal(&Cond_Car);

    // 互斥锁 解锁
    pthread_mutex_unlock(&Mutex_Car);
    return NULL;
}
void *Car_func(void *val)   // 车辆函数
{
    sleep(1);

    // P操作 -1
    sem_wait(&Sem_Car);

    // 互斥锁 上锁
    pthread_mutex_lock(&Mutex_Car);

    while (!sem_num)
    {
        pthread_cond_wait(&Cond_Car , &Mutex_Car);
    }

    // 开始停车
    printf("%d 号 汽车 开始 停车 \n" , *((int *)val));
    
    // 互斥锁 解锁
    pthread_mutex_unlock(&Mutex_Car);

    sleep(7);

    // 开始出场
    printf("%d 号 汽车 开始 出场 \n" , *((int *)val));
    sleep(2);

    // V操作 释放 车位
    sem_post(&Sem_Car);

    return NULL;
}

